#include "main.h"

bool programRunning;

// Main Window variables
Timer mainWindowTimer;
int mainWindowWidth;
int mainWindowHeight;
int mainWindowX;
int mainWindowY;
double mainWindowTimePerFrame;

// SubWindow variables
// LeftSubWindow
SubWindow leftSubWindow;

// RightSubWindow
SubWindow rightSubWindow;

// BottomSubWindow
SubWindow bottomSubWindow;

// PhotonEngine variables
PhotonEngine::Engine photonEngine;

#ifdef _WINDOWS
	HWND mainWindowHWND;
	HINSTANCE programHInstance;
	HMENU mainWindowMenu;
	HWND toolbarHWND;
#endif

#ifdef _WINDOWS
/**
	Function: LeftWindowProc
	Parameters:
		hwnd = window's hwnd (should be the left window's HWND
		message = message from Win32 about what happened to/in the window
		wparam = wparam of the message
		lparam = lparam of the message
	Description:
		Handles all the commands for the LeftWindow.
*/
INT_PTR CALLBACK LeftWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	switch (message) {
	case WM_MOVE:
		MoveWindow(leftSubWindow.window, leftSubWindow.x, leftSubWindow.y, leftSubWindow.width, leftSubWindow.height, TRUE);
		break;
	case WM_SIZE:
		break;
	default:
		return FALSE;
	}
	
	return TRUE;
}

/**
	Function: RightWindowProc
	Parameters:
		hwnd = right window hwnd
		message = message from Win32 about what happened to/in the window
		wparam = wparam of the message
		lparam = lparam of the message
	Description:
		Handles all the commands for the RightWindow.
*/
INT_PTR CALLBACK RightWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	switch (message) {
	case WM_MOVE:
		MoveWindow(rightSubWindow.window, rightSubWindow.x, rightSubWindow.y, rightSubWindow.width, rightSubWindow.height, TRUE);
		break;
	case WM_SIZE:
		break;
	default:
		return FALSE;
	}
	
	return TRUE;
}

/**
	Function: BottomWindowProc
	Parameters:
		hwnd = bottom window hwnd
		message = message from Win32 about what happened to/in the window
		wparam = wparam of the message
		lparam = lparam of the message
	Description:
		Handles all the commands for the BottomWindow.
*/
INT_PTR CALLBACK BottomWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	switch (message) {
	case WM_MOVE:
		MoveWindow(bottomSubWindow.window, bottomSubWindow.x, bottomSubWindow.y, bottomSubWindow.width, bottomSubWindow.height, TRUE);
		break;
	case WM_SIZE:
		break;
	default:
		return FALSE;
	}
	
	return TRUE;
}

/**
	Function: PhotonEngineProc
	Parameters:
		hwnd = photonEngine window hwnd
		message = message from Win32 about what happened to/in the window
		wparam = wparam of the message
		lparam = lparam of the message
	Description:
		Handles all the commands for the PhotonEngineWindow.
*/
LRESULT CALLBACK PhotonEngineProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	switch (message) {
	case WM_SIZE:  // If our window is resizing
		break;
	case WM_DESTROY:  // If our window is being closed
		PostQuitMessage(0);
		break;
	case WM_KEYDOWN:		
		break;
	case WM_KEYUP:
		break;
	case WM_MOUSEMOVE:
		break;
	case WM_LBUTTONDOWN:
		break;
	case WM_RBUTTONDOWN:
		break;
	case WM_RBUTTONUP:
		break;
	case WM_MBUTTONDOWN:
		break;
	}
	return DefWindowProc(hwnd, message, wparam, lparam);
}

/** 
	Function: MainWindowProc
	Parameters:
		hwnd = the window's hwnd
		message = Windows's message on what happened to the window
		wparam = wparam, more info about message
		lparam = lparam, more info about message
	Description:
		Handles all the commands that happen to the MainWindow.
*/
LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	switch (message) {
	case WM_SIZE:  // If our window is resizing
		mainWindowWidth = LOWORD(lparam);
		mainWindowHeight = HIWORD(lparam);
		for (int i = 0; i < 3; i++) {
			leftSubWindow.reshape(0, 30, 200, mainWindowHeight - 230);
			rightSubWindow.reshape(mainWindowWidth - 200, 30, 200, mainWindowHeight -230);
			bottomSubWindow.reshape(0, mainWindowHeight - 200, mainWindowWidth, 200);
			photonEngine.reshape(210, 30, mainWindowWidth - 420, mainWindowHeight - 240);
		}
		break;
	case WM_DESTROY:  // If our window is being closed
		programRunning = false;
		PostQuitMessage(0);
		break;
	case WM_COMMAND:
		switch (wparam) {
		case IDM_FILE1:
			// Open up the file window and select a file to load
			break;
		default:
			break;
		}
		break;
	case WM_KEYDOWN:		
		break;
	case WM_KEYUP:
		break;
	case WM_MOUSEMOVE:
		break;
	case WM_LBUTTONDOWN:
		break;
	case WM_RBUTTONDOWN:
		break;
	case WM_RBUTTONUP:
		break;
	case WM_MBUTTONDOWN:
		break;
	}
	return DefWindowProc(hwnd, message, wparam, lparam);
}

/**
	Function: createToolbarButton
	Parameters:
		*tbrButtons = pointer to the array of toolbar buttons that the created button will be sent
		place = place in the array
		fileImage = the file image found in the CommonControls
		ImageListID = image list id
		command = what command the button will send to the toolbar window when pressed
		style = style of button
		toolbarWindow = the window that the button resides in
		tooltipText = the text that will display above the button
	Description:
		Creates a toolbar button and places it inside an already create array of buttons.
*/
bool createToolbarButton(TBBUTTON *tbrButtons, int place, int fileImage, const int ImageListID, int command, BYTE style, HWND toolbarWindow, LPSTR tooltipText) {
	tbrButtons[place].iBitmap = MAKELONG(fileImage, ImageListID);
	tbrButtons[place].idCommand = command;
	tbrButtons[place].fsState = TBSTATE_ENABLED;
	tbrButtons[place].fsStyle = (const BYTE)(BTNS_AUTOSIZE | TBSTYLE_TOOLTIPS);
	tbrButtons[place].iString = SendMessage(toolbarWindow, TB_ADDSTRING, 0, (LPARAM)tooltipText);
	return true;
}

#endif

/**
	Function: setToolbarButtons
	Parameters:
		Nothing
	Description:
		Sets up and creates the toolbars for the program.
*/
void setToolbarButtons() {
#ifdef _WINDOWS
	INITCOMMONCONTROLSEX InitCtrlEx;

	InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
	InitCtrlEx.dwICC = ICC_BAR_CLASSES;
	InitCommonControlsEx(&InitCtrlEx);

	TBBUTTON toolbarButtons[TOOLBAR_NUM_BUTTONS + TOOLBAR_NUM_SEPARATORS];
	const int ImageListID = 0;

	toolbarHWND = CreateWindowExA(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_TOOLTIPS | WS_VISIBLE, 0, 0, 0, 0, mainWindowHWND, NULL, programHInstance, NULL);

	// Create the image list
	HIMAGELIST imageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, TOOLBAR_NUM_BUTTONS, 0);

	// Set the image list
	SendMessage(toolbarHWND, TB_SETIMAGELIST, (WPARAM)ImageListID, (LPARAM)imageList);

	// load the buttons images
	SendMessage(toolbarHWND, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);

	// Prevent text from displaying
	SendMessage(toolbarHWND, TB_SETMAXTEXTROWS, 0, 0);
	SendMessage(toolbarHWND, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);

	createToolbarButton(toolbarButtons, 0, STD_FILEOPEN, ImageListID, IDM_OPEN1, (const BYTE)(BTNS_AUTOSIZE | TBSTYLE_TOOLTIPS), toolbarHWND, "Open");

	SendMessage(toolbarHWND, TB_ADDBUTTONS, (WPARAM)(TOOLBAR_NUM_BUTTONS + TOOLBAR_NUM_SEPARATORS), (LPARAM)&toolbarButtons);
	SendMessage(toolbarHWND, TB_AUTOSIZE, 0, 0);
	ShowWindow(toolbarHWND, TRUE);
#endif

}

/** 
	Function: createWindow
	Parameters:
		width = window's width
		height = window's height
		hInstance = instance of this program
	Description:
		Creates the main window.  Includes the toolbar and the main tool 
		windows.
*/
bool createMainWindow() {
#ifdef _WINDOWS
	WNDCLASSEX wcex; // the window class
	
	// Fill out the window class's parameters
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc = MainWindowProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = programHInstance;
	wcex.hIcon = LoadIcon(programHInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
	wcex.lpszMenuName = MAKEINTRESOURCE(NULL);
	wcex.lpszClassName = PROGRAM_TITLE;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	
	// Register the class
	if (!RegisterClassEx(&wcex)) {
		return false;
	}

	// Load the menu
	mainWindowMenu = LoadMenu(programHInstance, MAKEINTRESOURCEA(IDR_MENU1));
	
	// Create the HWND for the program
	mainWindowHWND = CreateWindowA(PROGRAM_TITLE, PROGRAM_TITLE, WS_OVERLAPPEDWINDOW, mainWindowX, mainWindowY, mainWindowWidth, mainWindowHeight, NULL, mainWindowMenu, programHInstance, NULL);

	setToolbarButtons();

	// Setup the SubWindows
	leftSubWindow = SubWindow(mainWindowHWND, programHInstance, LeftWindowProc, IDD_LEFTWINDOW, 0, 30, 433, 200);
	rightSubWindow = SubWindow(mainWindowHWND, programHInstance, RightWindowProc, IDD_RIGHTWINDOW, 1064, 30, 433, 200);
	bottomSubWindow = SubWindow(mainWindowHWND, programHInstance, BottomWindowProc, IDD_BOTTOMWINDOW, 0, 463, 200, 1264);

	// Start the PhotonEngine
	if (!photonEngine.createWindow(PhotonEngineProc, programHInstance, "PhotonEngineWindow", 200, 30, 200, 200, mainWindowHWND)) {
		return false;
	}

	ShowWindow(mainWindowHWND, SW_SHOW);
	UpdateWindow(mainWindowHWND);

	return true;
#endif

	return false;
}

/**
	Function: sleepForDesiredFPS
	Parameters:
		fps = desired fps
		timePerFrame = the time per this frame
	Description:
		Calculates how much the program should sleep to get the desired fps.
*/
void sleepForDesiredFPS(int fps, double timePerFrame) {
	double desiredTimePerFrame = (float)(1.0 / fps);
	double threadSleepTime = desiredTimePerFrame - timePerFrame;
	if (threadSleepTime < 0) {
		threadSleepTime = 0;
	}
#ifdef _WINDOWS
	Sleep((DWORD)(threadSleepTime * 1000));
#endif
}

void saveOptions() {
	// Open file
	std::ofstream optionsFile;
	optionsFile.open(EDITOROPTIONSOUTPUTFILE);
	if (optionsFile.is_open()) {
		optionsFile << "PhotonEngine Options\n";
		optionsFile << "Editor Options\n";
		optionsFile << "Editor Option: name = value\n";

		optionsFile << "MainWindowWidth:\n" + std::to_string(mainWindowWidth) + "\n";
		optionsFile << "MainWindowHeight:\n" + std::to_string(mainWindowHeight) + "\n";
		optionsFile << "MainWindowX:\n" + std::to_string(mainWindowX) + "\n";
		optionsFile << "MainWindowY:\n" + std::to_string(mainWindowY) + "\n";

	} else {
		
	}
	optionsFile.close();

	photonEngine.printOptions();
}

void loadOptions() {	
	std::ifstream optionsFile;
	optionsFile.open(EDITOROPTIONSOUTPUTFILE);
	std::string line;

	if (optionsFile.is_open()) {
		while (optionsFile.good()) {
			std::getline(optionsFile, line);
			if (line == std::string("MainWindowWidth:")) {
				std::getline(optionsFile, line);
				mainWindowWidth = atoi(line.c_str());
			} else if (line == std::string("MainWindowHeight:")) {
				std::getline(optionsFile, line);
				mainWindowHeight = atoi(line.c_str());
			} else if (line == std::string("MainWindowX:")) {
				std::getline(optionsFile, line);
				mainWindowX = atoi(line.c_str());
			} else if (line == std::string("MainWindowY:")) {
				std::getline(optionsFile, line);
				mainWindowY = atoi(line.c_str());
			}
		}
	}

	// now check options
	if (mainWindowHeight == 0) {
		mainWindowHeight = 720;
	}
	if (mainWindowWidth == 0) {
		mainWindowWidth = 1280;
	}
}

void setMenuItemsState() {
#ifdef _WINDOWS

#endif
}

#ifdef _WINDOWS
/** 
	Function: WinMain
	Parameters:
		hinstance = handle for this instance of this progarm, used by Windows
		hPrevInstance = always NULL
		lpCmdLine = pointer string for any command-line arguments
		nCmdShow = determines how the window will be displayed
	Description:
		This function is called first and it sets up and runs the program.
		Everything is done in this function.
*/
#pragma warning(suppress: 28251)
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	// Windows message
	MSG msg;

	programHInstance = hInstance;

	loadOptions();

	// Start up the systems
	// Create Main Window
	if (!createMainWindow()) {
		programRunning = false;
	} else {
		programRunning = true;
	}

	// Setup the PhotonEngine
	photonEngine.loadOptions();
	photonEngine.start();

	// Set all the menu Items to their initial state
	setMenuItemsState();

	while (programRunning) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			if (msg.message == WM_QUIT) {
				programRunning = false;
			} else {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		mainWindowTimer.start();

		// do stuff related to editor

		// PhotonEngine stuff
		// Render Frame
		photonEngine.renderFrame();

		mainWindowTimer.stop();
		mainWindowTimePerFrame = mainWindowTimer.getElapsedTime();
		sleepForDesiredFPS(120, mainWindowTimePerFrame);
	}

	photonEngine.shutDown();

	saveOptions();

	#pragma warning(suppress: 6001)
	return (int) msg.wParam;
}
#endif